package com.lancoo.edu.platform.operation.backupRestore.service.bo;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.lancoo.edu.platform.operation.backupRestore.constant.BackupRestoreConstant;
import com.lancoo.edu.platform.operation.backupRestore.constant.DataBaseTypeEnum;
import com.lancoo.edu.platform.operation.backupRestore.constant.DateBaseStrategyEnum;
import com.lancoo.edu.platform.operation.backupRestore.domain.po.BrBackupInfo;
import com.lancoo.edu.platform.operation.backupRestore.domain.po.BrRestoreInfo;
import com.lancoo.edu.platform.operation.backupRestore.domain.po.BrServerSysInfo;
import com.lancoo.edu.platform.operation.backupRestore.domain.po.BrSysDatabaseInfo;
import com.lancoo.edu.platform.operation.backupRestore.service.po.BrBackupInfoService;
import com.lancoo.edu.platform.operation.backupRestore.service.po.BrRestoreInfoService;
import com.lancoo.edu.platform.operation.backupRestore.service.po.BrServerSysInfoService;
import com.lancoo.edu.platform.operation.backupRestore.service.po.BrSysDatabaseInfoService;
import com.lancoo.edu.platform.operation.backupRestore.strategy.backupRestore.BackupRestoreStrategy;
import com.lancoo.edu.platform.operation.backupRestore.strategy.backupRestore.imp.DmBackupRestoreStrategy;
import com.lancoo.edu.platform.operation.backupRestore.strategy.backupRestore.imp.MysqlBackupRestoreStrategy;
import com.lancoo.edu.platform.operation.backupRestore.strategy.backupRestore.imp.OceanBaseBackupRestoreStrategy;
import com.lancoo.edu.platform.operation.commons.config.DatabaseConfig;
import com.lancoo.edu.platform.operation.commons.config.LancooConfig;
import com.lancoo.edu.platform.operation.commons.dto.ConditionFactory;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StopWatch;

import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @author lzh
 * @Date 2024/4/7 8:52
 */
@AllArgsConstructor
@Service
@Slf4j
public class BrRestoreBoService {

    private final BrBackupInfoService brBackupInfoService;
    private final BrSysDatabaseInfoService brSysDatabaseInfoService;
    private final BrServerSysInfoService brServerSysInfoService;
    private final LancooConfig lancooConfig;
    private final BrRestoreInfoService brRestoreInfoService;
    private final DatabaseConfig databaseConfig;


    /**
     * 还原单个方法
     *
     * @param id          要恢复的备份点ID
     * @param userId      用户ID
     */
    // @Transactional(rollbackFor = Exception.class)
    // public void restoreSingle_pre(Integer id, String userId) {
    //     // 1、查询需要备份的系统对应数据库列表 和 要恢复的备份点
    //     BrBackupInfo brBackupInfo = brBackupInfoService.getById(id);
    //     ConditionFactory.getInstance().throwServiceException(Objects.isNull(brBackupInfo), "无法还原请联系管理员");
    //     BrServerSysInfo brServerSysInfo = brServerSysInfoService.getInfoBySysId(brBackupInfo.getEduId(), brBackupInfo.getSysId());
    //     ConditionFactory.getInstance().throwServiceException(Objects.isNull(brServerSysInfo), "无法还原请联系管理员");
    //     BrSysDatabaseInfo brSysDatabaseInfo = brSysDatabaseInfoService.getInfo(brBackupInfo.getSysId());
    //     ConditionFactory.getInstance().throwServiceException(Objects.isNull(brSysDatabaseInfo), "未找到备份点请联系管理员");
    //     // 2、判断是可以进行单个系统的恢复
    //     ConditionFactory.getInstance().throwServiceException(Objects.equals(brSysDatabaseInfo.getBeRestore(), BackupRestoreConstant.NO_RESTORE), "该系统不能单独还原");
    //     // 3、还原
    //     StopWatch stopWatch = new StopWatch();
    //     stopWatch.start();
    //     restoreForDatabaseType(brServerSysInfo.getServerIp(), brBackupInfo);
    //     stopWatch.stop();
    //     log.info("【还原耗时：{}，备份记录ID：{}】", stopWatch.getLastTaskTimeMillis(), id);
    //     //3、保存还原记录
    //     BrRestoreInfo brRestoreInfo = new BrRestoreInfo();
    //     brRestoreInfo.setRestoreTime(new Date());
    //     brRestoreInfo.setSysId(brBackupInfo.getSysId());
    //     brRestoreInfo.setEduId(brBackupInfo.getEduId());
    //     brRestoreInfo.setUserId(userId);
    //     brRestoreInfoService.save(brRestoreInfo);
    // }

    /**
     * 还原指定时间点的全部系统备份数据
     *
     * @param eduId   教育局ID
     * @param backupTime 备份时间点
     * @param userId     用户ID
     */
    // @Transactional(rollbackFor = Exception.class)
    // public void restoreAllByBackupTime_pre(String eduId, Date backupTime, String userId) {
    //     // 1、查询需要备份的系统对应数据库列表 和 要恢复的备份点列表
    //     DateTime startTime = DateUtil.beginOfDay(backupTime);
    //     DateTime endTime = DateUtil.endOfDay(backupTime);
    //     List<BrBackupInfo> backupInfoList = brBackupInfoService.getList(eduId, startTime, endTime);
    //     List<BrServerSysInfo> brServerSysInfoList = brServerSysInfoService.getListByEduId(eduId);
    //     ConditionFactory.getInstance().throwServiceException(CollectionUtil.isEmpty(brServerSysInfoList) || CollectionUtil.isEmpty(backupInfoList), "无法还原请联系管理员");
    //     Map<String, String> sysMapServer = brServerSysInfoList.stream().collect(Collectors.groupingBy(BrServerSysInfo::getSysId, Collectors.mapping(BrServerSysInfo::getServerIp, Collectors.joining())));
    //     // 2、循环还原
    //     StopWatch stopWatch = new StopWatch();
    //     stopWatch.start();
    //     for (BrBackupInfo brBackupInfo : backupInfoList) {
    //         if (sysMapServer.containsKey(brBackupInfo.getSysId())) {
    //             restoreForDatabaseType(sysMapServer.get(brBackupInfo.getSysId()), brBackupInfo);
    //         }
    //     }
    //     stopWatch.stop();
    //     log.info("【还原耗时：{}，备份记录时间点：{}】", stopWatch.getLastTaskTimeMillis(), backupTime);
    //     //3、保存还原记录
    //     BrRestoreInfo brRestoreInfo = new BrRestoreInfo();
    //     brRestoreInfo.setRestoreTime(new Date());
    //     brRestoreInfo.setSysId("all");
    //     brRestoreInfo.setEduId(eduId);
    //     brRestoreInfo.setUserId(userId);
    //     brRestoreInfo.setRestoreBackupTime(backupTime);
    //     brRestoreInfoService.save(brRestoreInfo);
    // }

    /**
     * 还原单个方法
     *
     * @param id          要恢复的备份点ID
     * @param userId      用户ID
     */
    @Transactional(rollbackFor = Exception.class)
    public void restoreSingle(Integer id, String userId) {
        // 1、查询需要备份的系统对应数据库列表 和 要恢复的备份点
        BrBackupInfo brBackupInfo = brBackupInfoService.getById(id);
        ConditionFactory.getInstance().throwServiceException(Objects.isNull(brBackupInfo), "无法还原请联系管理员");
        BrServerSysInfo brServerSysInfo = brServerSysInfoService.getInfoBySysId(brBackupInfo.getEduId(), brBackupInfo.getSysId());
        ConditionFactory.getInstance().throwServiceException(Objects.isNull(brServerSysInfo), "还原失败，未获取到系统数据库IP");
        BrSysDatabaseInfo brSysDatabaseInfo = brSysDatabaseInfoService.getInfo(brBackupInfo.getSysId());
        ConditionFactory.getInstance().throwServiceException(Objects.isNull(brSysDatabaseInfo), "未找到备份点请联系管理员");
        // 2、判断是可以进行单个系统的恢复
        ConditionFactory.getInstance().throwServiceException(Objects.equals(brSysDatabaseInfo.getBeRestore(), BackupRestoreConstant.NO_RESTORE), "该系统不能单独还原");
        // 3、还原
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        String result = restore(brBackupInfo.getBackupFilePath(), brBackupInfo.getDatabaseName(), brBackupInfo.getSysId(), brBackupInfo.getEduId(), brServerSysInfo.getServerIp());
        stopWatch.stop();
        log.info("【还原耗时：{}，备份记录ID：{}】", stopWatch.getLastTaskTimeMillis(), id);
        //3、保存还原记录
        BrRestoreInfo brRestoreInfo = new BrRestoreInfo();
        brRestoreInfo.setRestoreTime(new Date());
        brRestoreInfo.setSysId(brBackupInfo.getSysId());
        brRestoreInfo.setEduId(brBackupInfo.getEduId());
        brRestoreInfo.setRestoreResult(result);
        brRestoreInfo.setUserId(userId);
        brRestoreInfo.setBackupUniqueId(brBackupInfo.getBackupUniqueId());
        brRestoreInfoService.save(brRestoreInfo);
    }

    /**
     * 还原指定备份唯一ID的全部系统备份数据
     *
     * @param eduId       教育局ID
     * @param backupUniqueId 备份唯一ID
     * @param userId         用户ID
     */
    @Transactional(rollbackFor = Exception.class)
    public void restoreAllByBackupUniqueId(String eduId, Long backupUniqueId, String userId) {
        // 1、查询需要备份的系统对应数据库列表 和 要恢复的备份点列表
        List<BrBackupInfo> backupInfoList = brBackupInfoService.getListByBackupUniqueId(eduId, backupUniqueId);
        List<BrServerSysInfo> brServerSysInfoList = brServerSysInfoService.getListByEduId(eduId);
        ConditionFactory.getInstance().throwServiceException(CollectionUtil.isEmpty(brServerSysInfoList) || CollectionUtil.isEmpty(backupInfoList), "无法还原请联系管理员");
        Map<String, String> sysMapServer = brServerSysInfoList.stream().collect(Collectors.groupingBy(BrServerSysInfo::getSysId, Collectors.mapping(BrServerSysInfo::getServerIp, Collectors.joining())));
        // 2、循环还原
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        String result = BackupRestoreConstant.ERROR_BOOLEAN;
        for (BrBackupInfo brBackupInfo : backupInfoList) {
            if (sysMapServer.containsKey(brBackupInfo.getSysId())) {
                result = restore(brBackupInfo.getBackupFilePath(), brBackupInfo.getDatabaseName(), brBackupInfo.getSysId(), brBackupInfo.getEduId(), sysMapServer.get(brBackupInfo.getSysId()));
            }
        }
        stopWatch.stop();
        log.info("【还原耗时：{}，备份记录唯一ID：{}】", stopWatch.getLastTaskTimeMillis(), backupUniqueId);
        //3、保存还原记录
        BrRestoreInfo brRestoreInfo = new BrRestoreInfo();
        brRestoreInfo.setRestoreTime(new Date());
        brRestoreInfo.setSysId("all");
        brRestoreInfo.setEduId(eduId);
        brRestoreInfo.setUserId(userId);
        brRestoreInfo.setRestoreResult(result);
        brRestoreInfo.setRestoreBackupTime(new Date());
        brRestoreInfo.setBackupUniqueId(backupUniqueId);
        brRestoreInfoService.save(brRestoreInfo);
    }


    /**
     * 根据不同数据库类型进行还原
     *
     * @param serverIp     系统对应服务器IP
     * @param brBackupInfo 备份点
     */
    // private void restoreForDatabaseType(String serverIp, BrBackupInfo brBackupInfo) {
    //     switch (DataBaseTypeEnum.getByType(lancooConfig.getDatabaseType())) {
    //         case MYSQL:
    //             MysqlBackupRestoreStrategy mysqlBackupStrategy = new MysqlBackupRestoreStrategy();
    //             String mysqlDump = DataBaseTypeEnum.MYSQL.getDatabaseDump();
    //             restore(mysqlBackupStrategy, brBackupInfo.getBackupFilePath(), brBackupInfo.getDatabaseName(), brBackupInfo.getSysId(), brBackupInfo.getEduId(), serverIp, mysqlDump);
    //             break;
    //         case DM:
    //             DmBackupRestoreStrategy dmBackupStrategy = new DmBackupRestoreStrategy();
    //             String dmDump = DataBaseTypeEnum.DM.getDatabaseDump();
    //             restore(dmBackupStrategy, brBackupInfo.getBackupFilePath(), brBackupInfo.getDatabaseName(), brBackupInfo.getSysId(), brBackupInfo.getEduId(), serverIp, dmDump);
    //             break;
    //         case OCEAN_BASE:
    //             OceanBaseBackupRestoreStrategy oceanBaseBackupStrategy = new OceanBaseBackupRestoreStrategy();
    //             String oceanBaseDump = DataBaseTypeEnum.OCEAN_BASE.getDatabaseDump();
    //             restore(oceanBaseBackupStrategy, brBackupInfo.getBackupFilePath(), brBackupInfo.getDatabaseName(), brBackupInfo.getSysId(), brBackupInfo.getEduId(), serverIp, oceanBaseDump);
    //             break;
    //         default:
    //             break;
    //     }
    // }

    /**
     * 还原方法
     *
     * @param backupRestoreStrategy 备份策略
     * @param savePath              还原文件绝对地址
     * @param sysId                 系统ID
     * @param dumpPath              数据库bin路径
     */
    public void restore_pre(BackupRestoreStrategy backupRestoreStrategy, String savePath, String databaseName, String sysId, String eduId, String host, String dumpPath) {
        String saveDir = lancooConfig.getBackSavePath() + "/" + sysId + "/" + eduId + "/";
        backupRestoreStrategy.restore(sysId, databaseName, savePath, saveDir, dumpPath, databaseConfig.getUserName(), databaseConfig.getPassword(), host, databaseConfig.getPort());
    }

    /**
     * 还原方法
     *
     * @param savePath 还原文件绝对地址
     * @param sysId    系统ID
     */
    public String restore(String savePath, String databaseName, String sysId, String eduId, String host) {
        String databaseType = lancooConfig.getDatabaseType();
        String systemEnv = lancooConfig.getSystemEnv();
        DateBaseStrategyEnum dateBaseStrategyEnum = DateBaseStrategyEnum.getStrategy(databaseType);
        BackupRestoreStrategy backupRestoreStrategy = dateBaseStrategyEnum.getBackupRestoreStrategy();
        String databaseDump = DataBaseTypeEnum.getByType(databaseType, systemEnv).getDatabaseDump();
        String saveDir = lancooConfig.getBackSavePath() + "/" + sysId + "/" + eduId + "/";
        return backupRestoreStrategy.restore(sysId, databaseName, savePath, saveDir, databaseDump, databaseConfig.getUserName(), databaseConfig.getPassword(), host, databaseConfig.getPort());
    }
}
